﻿using System;
using System.Text.RegularExpressions;
using Velocity4Net.Runtime.Parser;
using Velocity4Net.Util;
using Velocity4Net.Util.Introspection;

namespace Velocity4Net.Errors
{

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */








    /**
     *  Application-level exception thrown when a resource of any type
     *  has a syntax or other error which prevents it from being parsed.
     *  <br>
     *  When this resource is thrown, a best effort will be made to have
     *  useful information in the exception's message.  For complete
     *  information, consult the runtime log.
     *
     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
     * @author <a href="hps@intermeta.de">Henning P. Schmiedehausen</a>
     * @version $Id: ParseErrorException.java 1866609 2019-09-08 10:42:47Z cbrisson $
     */
    public class ParseErrorException : VelocityException
    {
        /**
         * The column number of the parsing error, or -1 if not defined.
         */
        private int columnNumber = -1;

        /**
         * The line number of the parsing error, or -1 if not defined.
         */
        private int lineNumber = -1;

        /**
         * The name of the template containing the error, or null if not defined.
         */
        private String templateName = "*unset*";

        /**
         * If applicable, contains the invalid syntax or reference that triggered this exception
         */
        private String invalidSyntax;

        /**
         * If we modify the message, then we set this
         */
        private String msg = null;

        /**
         * Create a ParseErrorException with the given message.
         *
         * @param exceptionMessage the error exception message
         */
        public ParseErrorException(String exceptionMessage) : base(exceptionMessage)
        {
        }

        private readonly Regex lexError = new Regex("Lexical error.*TokenMgrError.*line (\\d+),.*column (\\d+)\\.(.*)");

        /**
         * Create a ParseErrorException with the given ParseException.
         *
         * @param pex the parsing exception
         * @param templName
         * @since 1.5
         */
        public ParseErrorException(ParseException pex, String templName) : base(pex.Message)
        {

            if (templName != null) templateName = templName;

            // Don't use a second C'tor, TemplateParseException is a subclass of
            // ParseException...
            if (pex is ExtendedParseException)
            {
                ExtendedParseException xpex = (ExtendedParseException)pex;

                columnNumber = xpex.getColumnNumber();
                lineNumber = xpex.getLineNumber();
                templateName = xpex.getTemplateName();
            }
            else
            {
                // We get here if the the Parser has thrown an exception. Unfortunately,
                // the error message created is hard coded by javacc, so here we alter
                // the error message, so that it is in our standard format.
                Match match = lexError.Match(pex.getMessage());
                if (match.Success)
                {
                    lineNumber = Convert.ToInt32(match.Groups[1].Value);
                    columnNumber = Convert.ToInt32(match.Groups[2].Value);
                    String restOfMsg = match.Groups[3].Value;
                    msg = "Lexical error, " + restOfMsg + " at "
                      + StringUtils.formatFileString(templateName, lineNumber, columnNumber);
                }

                //  ugly, ugly, ugly...

                if (pex.currentToken != null && pex.currentToken.next != null)
                {
                    columnNumber = pex.currentToken.next.beginColumn;
                    lineNumber = pex.currentToken.next.beginLine;
                }
            }
        }

        /**
         * Create a ParseErrorException with the given ParseException.
         *
         * @param pex the parsing exception
         * @param templName
         * @since 1.5
         */
        public ParseErrorException(VelocityException pex, String templName) : base(pex.Message)
        {
            if (templName != null) templateName = templName;

            // Don't use a second C'tor, TemplateParseException is a subclass of
            // ParseException...
            if (pex is ExtendedParseException)
            {
                ExtendedParseException xpex = (ExtendedParseException)pex;

                columnNumber = xpex.getColumnNumber();
                lineNumber = xpex.getLineNumber();
                templateName = xpex.getTemplateName();
            }
            else if (pex.InnerException is ParseException)
            {
                ParseException pex2 = (ParseException)pex.InnerException;

                if (pex2.currentToken != null && pex2.currentToken.next != null)
                {
                    columnNumber = pex2.currentToken.next.beginColumn;
                    lineNumber = pex2.currentToken.next.beginLine;
                }
            }
        }


        /**
         * Create a ParseErrorSystemException with the given message and info
         *
         * @param exceptionMessage the error exception message
         * @param info an Info object with the current template info
         * @since 1.5
         */
        public ParseErrorException(String exceptionMessage, Info info) : base(exceptionMessage)
        {
            columnNumber = info.getColumn();
            lineNumber = info.getLine();
            templateName = info.getTemplateName();
        }

        /**
         * Create a ParseErrorSystemException with the given message and info
         *
         * @param exceptionMessage the error exception message
         * @param info an Info object with the current template info
         * @since 2.2
         */
        public ParseErrorException(String exceptionMessage, Info info, String[] stacktrace)
      : base(exceptionMessage, null, stacktrace)
        {
            columnNumber = info.getColumn();
            lineNumber = info.getLine();
            templateName = info.getTemplateName();
        }

        /**
         * Create a ParseErrorSystemException with the given message and info
         *
         * @param exceptionMessage the error exception message
         * @param info an Info object with the current template info
         * @param invalidSyntax the invalid syntax or reference triggering this exception
         * @since 1.5
         */
        public ParseErrorException(String exceptionMessage, Info info, String invalidSyntax) : base(exceptionMessage)
        {
            columnNumber = info.getColumn();
            lineNumber = info.getLine();
            templateName = info.getTemplateName();
            this.invalidSyntax = invalidSyntax;
        }


        /**
         * Return the column number of the parsing error, or -1 if not defined.
         *
         * @return column number of the parsing error, or -1 if not defined
         * @since 1.5
         */
        public int getColumnNumber()
        {
            return columnNumber;
        }

        /**
         * Return the line number of the parsing error, or -1 if not defined.
         *
         * @return line number of the parsing error, or -1 if not defined
         * @since 1.5
         */
        public int getLineNumber()
        {
            return lineNumber;
        }

        /**
         * Return the name of the template containing the error, or null if not
         * defined.
         *
         * @return the name of the template containing the parsing error, or null
         *      if not defined
         * @since 1.5
         */
        public String getTemplateName()
        {
            return templateName;
        }

        /**
         * Return the invalid syntax or reference that triggered this error, or null
         * if not defined.
         *
         * @return Return the invalid syntax or reference that triggered this error, or null
         * if not defined
         * @since 1.5
         */
        public String getInvalidSyntax()
        {
            return invalidSyntax;
        }

        /**
         * Return our custom message if we have one, else return the default message
         */
        public String getMessage()
        {
            if (msg != null) return msg;
            return base.Message;
        }

        public override string Message => getMessage();
    }
}